/* directorios.c
*  Created on: 09/10/2011
 *      Author: Normandia  */

#include "directorios.h"

int ultimoNodo = 0;
char closure_path[14];


int getProxIdTDOpen(){
         return ultimoNodo++;
}

int set_getattr(struct stat *st,const char *path){

	OPENFILE * fatFile;

	memset(st,0,sizeof(struct stat));

	fatFile = getArchivoTD(path);
	if (!fatFile)
			 return -2;

	 if(!strcmp(path, "/")) {
			  st->st_ino = cache_boot_sector->n_cluster_root_dir;
			  st->st_mode = S_IFDIR | 0777;
			  st->st_size = 0;
			  st->st_nlink = 2;
			  //Hay que ver por que y como solucionar esto
			  //st->st_ctim.tv_sec = st->st_atim.tv_sec = st->st_mtim.tv_sec = 0;
	 } else {
			st->st_ino = fatFile->primerCluster;

			if ( fatFile->atributos == DT_FILEATRIBUTES_SUBDIRECTORY ) {
				  st->st_mode = S_IFDIR | 0777;
				  st->st_nlink = 2;
				  st->st_size = 0;
			} else {
				  st->st_mode = S_IFREG | 0777;
				  st->st_nlink = 1;
				  st->st_size = fatFile->size;
			}

			st->st_blksize = 4096;
			st->st_blocks = (fatFile->size / st->st_blksize) + 1;
			//st->st_ctim.tv_sec = st->st_atim.tv_sec = st->st_mtim.tv_sec = pfs_fat32_utils_getTime(&(fatFile->shortEntry));
			//st->st_ctim.tv_sec = st->st_atim.tv_sec = st->st_mtim.tv_sec = 0;

	 }
	 free(fatFile);
	 return 0;


}

void destroyList(t_list* listDirectoryTable){
	collection_list_destroy(listDirectoryTable,free);
}

OPENFILE* getArchivoTD(const char *path){
          char * pch = NULL;
          char barra[2] = "/";
          char path2[strlen(path)+1];

      t_list* listDirectoryTable;
          OPENFILE * nodoTD,res;

          memcpy(path2,path,strlen(path)+1);

          if(!strcmp(path2, barra)) {

                  nodoTD = malloc(SIZE_OPENFILE);
                  memset(nodoTD, '\0', SIZE_OPENFILE);
                  nodoTD->atributos = 0x10;
                  nodoTD->size = SECTOR_SIZE * 8;
                  nodoTD->primerCluster = 2;

                  return nodoTD;

          }

          listDirectoryTable = getDirectoryTable(cache_boot_sector->n_cluster_root_dir);

          //strcpy(path2, path);

          pch = strtok(path2, barra);

          while (pch != NULL)
          {
                  if ((nodoTD = getNodoDirectoryTable(listDirectoryTable, pch)) != NULL){
                          pch = strtok(NULL, "/");
                          destroyList(listDirectoryTable);
                          if (pch != NULL){
                                listDirectoryTable = getDirectoryTable(nodoTD->primerCluster);
                                free(nodoTD);
                          }

                  }else{
                          destroyList(listDirectoryTable);
                          return NULL;
                  }

         }

         return nodoTD;
}

t_list* getDirectorioTD(const char *path){
          char * pch = NULL;
          char barra[] = "/";
          char path2 [strlen(path)+1];

          t_list* listDirectoryTable;
          OPENFILE * nodoTD;

          listDirectoryTable = getDirectoryTable(cache_boot_sector->n_cluster_root_dir);

          strcpy(path2, path);
          pch = strtok(path2, barra);

          if (pch == NULL)
                          return listDirectoryTable;

          while (pch != NULL)
          {
                  if ((nodoTD  = getNodoDirectoryTable(listDirectoryTable, pch)) != NULL){
                          destroyList(listDirectoryTable);

                          pch = strtok(NULL, "/");
                          listDirectoryTable = getDirectoryTable(nodoTD->primerCluster);

                    	  free(nodoTD);
                          if (pch == NULL){
                        	  return listDirectoryTable;
                          }


                  }else{
						destroyList(listDirectoryTable);
						return NULL;
                  }

         }

         return NULL;

}

OPENFILE* getNodoDirectoryTable(t_list* list, char *pch ){

	OPENFILE* resultado, *aux;

	int closureDirectoryTable (void * nodo){
		OPENFILE* nodoTD = (OPENFILE*)nodo;

		return !(strcmp(pch, nodoTD->fileName));

	}

	aux = (OPENFILE*)collection_list_find( list, closureDirectoryTable);

	if(!aux)
		return NULL;

	resultado = malloc(SIZE_OPENFILE);
	memcpy(resultado,aux,SIZE_OPENFILE);

	return resultado;
}

/*
int closureDirectoryTable (void * nodo){
        OPENFILE* nodoTD = (OPENFILE*)nodo;

        return !(strcmp(closure_path, nodoTD->fileName));

        memset(closure_path, '\0', strlen(closure_path));

}*/

t_list* getDirectoryTable(uint32_t cluster){

        t_list* l_td = NULL;
        DIRECTORYTABLE nodoTD;
        DIRECTORYLONGNAME nodoLFN;
        OPENFILE* nodoOpenFile;
        void * clusterBuff;
        int i;
        int flag_longFile = VACIO;
        uint32_t utf8size;

        uint8_t firstByte;

        uint64_t clusterSize = cache_boot_sector->n_sectors_per_cluster * SECTOR_SIZE;
        uint64_t cantTDnodes = clusterSize / SIZE_TD;

        char* nameAux = NULL;

                if(l_td == NULL)
                        l_td = collection_list_create();

        clusterBuff = leerCluster(cluster);

        memset(&nodoLFN, '\0', SIZE_TD);
        memset(&nodoTD, '\0', SIZE_TD);

        for(i=0;i<cantTDnodes;i++){

                        memcpy(&firstByte, clusterBuff + i * SIZE_TD, 1);
                        if ((firstByte == DT_FILENAME_VALUE_ENTRY) || (firstByte == DT_FILENAME_DELETE))
                                continue;

                        memcpy(&nodoTD, clusterBuff + i * SIZE_TD, SIZE_TD);

                        if (nodoTD.fileAtributes == DT_FILEATRIBUTES_LONG_FILE){
                                memset(&nodoLFN, '\0', SIZE_LFN);
                                memcpy(&nodoLFN, clusterBuff + i * SIZE_TD, SIZE_LFN);
                                flag_longFile = LLENO;
                        }else
                        {
                                nodoOpenFile = malloc(SIZE_OPENFILE);
                                memset(nodoOpenFile, '\0', SIZE_OPENFILE);


                                if(flag_longFile == LLENO){
                                        //if ((nameAux = unicode_utf16_to_utf8(nodoLFN.fileName,SIZE_FILE_NAME / 2 ,&utf8size)) != NULL){
                                                arreglar_fucking_string((char*)nodoLFN.fileName,SIZE_FILE_NAME);
                                        if(icuc_string_to_utf8(&nameAux, &utf8size, SIZE_FILE_NAME / 2, nodoLFN.fileName) > 0) {
                                           strcat(nodoOpenFile->fileName, nameAux);
                                                free(nameAux);
                                                nameAux = NULL;
                                        }

                                        //if ((nameAux = unicode_utf16_to_utf8(nodoLFN.Name2,SIZE_NAME2 / 2 ,&utf8size)) != NULL){
                                        arreglar_fucking_string((char*)nodoLFN.Name2,SIZE_NAME2);
                                        icuc_string_to_utf8(&nameAux, &utf8size, SIZE_NAME2 / 2, nodoLFN.Name2);
                                        if ( nameAux[0] > 0 )    {
                                          strcat(nodoOpenFile->fileName, nameAux);
                                           free(nameAux);
                                           nameAux = NULL;
                                        }

                                        //if ((nameAux = unicode_utf16_to_utf8(nodoLFN.Name3,SIZE_NAME3 / 2 ,&utf8size)) != NULL){
                                        arreglar_fucking_string((char*)nodoLFN.Name3,SIZE_NAME3);
                                        icuc_string_to_utf8(&nameAux, &utf8size, SIZE_NAME3 / 2, nodoLFN.Name3) ;
                                        	if (nameAux[0] > 0) {
                                                strcat(nodoOpenFile->fileName, nameAux);
                                                free(nameAux);
                                                nameAux = NULL;
                                        }

                                        flag_longFile = VACIO;
                                }else{
                                        arreglar_fucking_string((char*)nodoTD.fileName,SIZE_FILE_NAME);
                                        if(firstByte == DT_FILENAME_DOT)
                                            memcpy(nodoOpenFile->fileName,nodoTD.fileName,2);
                                        else
                                        	strcpy(nodoOpenFile->fileName,armaConExt(nodoTD.fileName,nodoTD.fileExt));

                                }

//                                strcpy(nodoOpenFile->fileName, armaConExt(nodoOpenFile->fileName,nodoTD.fileExt));

                                nodoOpenFile->dirCluster    = cluster;
                                nodoOpenFile->idTD = i; // ID dentro de la tabla de directorio
                                nodoOpenFile->modificado = 0;
                                nodoOpenFile->atributos     = nodoTD.fileAtributes;
                                nodoOpenFile->primerCluster = convertFirstCluster(nodoTD.high2Bytes,nodoTD.low2Bytes);
                                nodoOpenFile->size          = convertFilesize(nodoTD.fileSize);
                                nodoOpenFile->vecesAbierto  = 0;
                                //                              char    fileExt[3];

                                collection_list_add(l_td,(void*)nodoOpenFile);

                        }

        }
        free(clusterBuff);

        return l_td;
}

char *armaConExt(char *fileName,uint8_t* fileExt){
    char* path2 =(char*)malloc(14);
    char nameAux[4];
    int i;

    if (fileExt[0] == 32)
     	return fileName;


    memset(path2, '\0', 14);
    memset(nameAux, '\0',4);
    strcpy(path2, fileName);
    memcpy(nameAux, fileExt,3);

    for(i = 0; i < strlen(path2); i++){
      if (path2[i] == ' ') {
    	  path2[i]   = '.';
    	  path2[i+1] = '\0';
    	  break;
      }

    }

   strcat(path2, nameAux);

   return path2;
}

uint32_t convertFilesize( uint32_t fileSize){
        return fileSize;
}

uint32_t convertFirstCluster( uint16_t high2Bytes,      uint16_t low2Bytes){

        uint32_t highAux;
        uint32_t lowAux;
        uint32_t res;

        highAux = high2Bytes<<16;
        lowAux  = low2Bytes;

        res = lowAux + highAux;

   return res;
}

int leerArchivo(char * ret, uint64_t fh, size_t size, off_t offset){
        char* buffer;
        uint32_t n_cluster; // Número de cluster a leer
        uint32_t primer_cluster;
        uint32_t n_offSetxSector;
        size_t n_bytes; // Bytes a leer del buffer
        off_t pos = 0; // Posición
        off_t offsetAux = 0;
        size_t n_readBytes;
        int retorno;
        int max_cache = configuracion.tamanio_cache / (cache_boot_sector->n_sectors_per_cluster * SECTOR_SIZE);

        //memset(ret, '0', size);

        primer_cluster	= ((OPENFILE*)fh)->primerCluster;
        n_offSetxSector = offset / (cache_boot_sector->n_sectors_per_cluster * SECTOR_SIZE);

        n_cluster = obtenerNCluster(primer_cluster, n_offSetxSector);

        n_readBytes = size;
        retorno = n_readBytes;
        offsetAux = offset;

        offsetAux = offsetAux % (cache_boot_sector->n_sectors_per_cluster * SECTOR_SIZE);

        while(n_readBytes>0 && n_cluster < DT_FILENAME_EOC){

        	buffer = leerClusterEnCache(fh, n_cluster);

        	if (buffer == NULL){
                buffer = leerCluster(n_cluster);
                agregarCache(buffer,fh,n_cluster);
        	}

			n_bytes = (cache_boot_sector->n_sectors_per_cluster * SECTOR_SIZE) - offsetAux;
			n_bytes = n_bytes > n_readBytes ? n_readBytes : n_bytes;

			memcpy(ret+pos, buffer + offsetAux, n_bytes);

			if (max_cache == 0)
				free(buffer);

			pos += n_bytes;
			offsetAux = 0;
			n_readBytes -= n_bytes;
			//if(n_cluster >= 0x0FFFFFF0) break;
			n_cluster = obtenerSigCluster(n_cluster);

        }

        return retorno;

}

void agregarCache(void *buffer,uint64_t fh, uint32_t n_cluster){

	//RECUPERAR DESDE PARAMETROS DE CONFIGURACION
	int b = 0;
	int max_cache = configuracion.tamanio_cache / (cache_boot_sector->n_sectors_per_cluster * SECTOR_SIZE);
	int size_cluster = 4092;
	OPENFILE *file = (OPENFILE*)fh;

	if(max_cache > 0){
		for(b=0;b<max_cache;b++){
			if (file->cache[b].orden == max_cache || file->cache[b].orden == 0 )
				break;
		}

		if (file->cache[b].buf != NULL){
			if(file->cache[b].modificado == 1){
				escribirCluster(file->cache[b].Cluster,file->cache[b].buf);

			}
			free(file->cache[b].buf);

		}

		file->cache[b].Cluster   = n_cluster;
		//file->cache[b].buf = malloc(size_cluster);
		file->cache[b].modificado = 0;
		file->cache[b].buf = buffer;
		//memcpy(file->cache[b].buf, buffer, size_cluster);
		cache_pasarAprimero(file, b);
	}

}

void cache_pasarAprimero(OPENFILE *file, int index){
	int b;

	int max_cache = configuracion.tamanio_cache / (cache_boot_sector->n_sectors_per_cluster * SECTOR_SIZE);
    for(b=0;b<max_cache;b++){
    	if (file->cache[b].orden != 0)
    		file->cache[b].orden++;
    }
	file->cache[index].orden = 1;

}

void dump_cache(int signal){
 	FILE *pfsLog; /* archivo donde se guardaran los log's */
	int b;
	char cadena[4094];
	char *zfile = "cache.txt";
	t_link_element *ptr;
	OPENFILE *data;
	int max_cache = configuracion.tamanio_cache / (cache_boot_sector->n_sectors_per_cluster * SECTOR_SIZE);

	cadena[4092] = '\0';
	cadena[4093] = '\0';

	printf("DUMP CACHE");

	unlink(zfile);
	if((pfsLog=fopen(zfile,"at"))==NULL){
		printf("Error al abrir archivo cache.\n\n");
	}
	ptr= listOpenFile->head;
	fprintf(pfsLog,"Cache1\n");

	if(ptr == NULL){
			printf("La cache está vacía.\n\n");
		}else{
			while(ptr != NULL){
				data = ptr->data;
				fprintf(pfsLog,"%s\n",data->fileName);
				b = 0;
				while(data->cache[b].buf != NULL && b < max_cache){
					fprintf(pfsLog,"%d\n",b);
					fprintf(pfsLog,"%s\n",data->cache[b].buf);
					b++;
				}

				ptr = ptr->next;
			}
		}

	fclose(pfsLog); /* cierre del archivo log */



}

void * leerClusterEnCache(uint64_t fh ,uint32_t n_cluster){

	int max_cache = configuracion.tamanio_cache / (cache_boot_sector->n_sectors_per_cluster * SECTOR_SIZE);
	int a = 0;
	OPENFILE *file = (OPENFILE*)fh;

	while(a < max_cache){
		if (file->cache[a].Cluster == n_cluster){

		  cache_pasarAprimero(file, a);

		  return file->cache[a].buf;
		}

		a++;
	}

	return NULL;
}

uint64_t abrirArchivo(const char *path){

        OPENFILE *tdArchivo;
        OPENFILE* abierto;
        uint32_t j, i, ret;
       	uint32_t cluster;
       	char cl_aux[20];
       	char clusters[500];
      	char texto[500];

        int b, max_cache = configuracion.tamanio_cache / (cache_boot_sector->n_sectors_per_cluster * SECTOR_SIZE);

        tdArchivo = getArchivoTD(path);

        if (tdArchivo == NULL)
                return -1;

        cluster = tdArchivo->primerCluster;
		i = tdArchivo->size / (cache_boot_sector->n_sectors_per_cluster * SECTOR_SIZE);

		if (i > 10)
			i = 10;

		for (j = 0; j < i; j++) {

			cluster = cache_fat[cluster];

			if (cluster >= DT_FILENAME_EOC)
				break;

			sprintf(clusters,"%u, ",cluster);
		}

		sprintf(texto,"se abrio archivo %s clusters: %s\n",tdArchivo->fileName,clusters);

	if(configuracion.log == '1')
			ret = log1("FileSystem", "logFS.txt", "INFO", texto);

        for(b=0;b<max_cache;b++){
        	tdArchivo->cache[b].orden   = 0;
        	tdArchivo->cache[b].Cluster = 0;
        	tdArchivo->cache[b].modificado = 0;
        	tdArchivo->cache[b].buf = NULL;
        }

        abierto = archivoAbierto((uint64_t) tdArchivo);
        if(abierto ){
        	abierto->vecesAbierto++;
        	return (uint64_t)abierto;
        }else{
            tdArchivo->Id = getProxIdTDOpen();
            tdArchivo->vecesAbierto = 1;
            //memset(tdArchivo->cache, '0', SIZE_CACHE_TABLE);
        }

        if(listOpenFile == NULL)
                listOpenFile = collection_list_create();

        collection_list_add(listOpenFile,(void*)tdArchivo);

        return (uint64_t)tdArchivo;
}


mode_t convertir_modo(uint8_t attr){
        mode_t res = 0777;
        uint8_t aux;

        aux = attr % 010;
        attr = attr - aux;
        aux = attr % 0100;

        switch(aux){
                case DT_FILEATRIBUTES_SUBDIRECTORY:
//                      res = S_IFDIR | res;
                        break;
                case DT_FILEATRIBUTES_ARCHIVE:
//                      res = S_IFREG | res;
                        break;
        }

        return res;

}

void arreglar_fucking_string(char* str, size_t tamanio) {

        // Trim a la derecha
        while(str[tamanio] == '\32' || str[tamanio] == '\255'){
                str[tamanio] = '\0';
                tamanio--;
        }

}

OPENFILE* archivoAbierto(uint64_t fh){

	OPENFILE* tdArchivo = (OPENFILE*) fh;
	OPENFILE* abierto;

    // Busca si el archivo ya estaba abierto
    int buscador(OPENFILE* data){

    	if(data->primerCluster == tdArchivo->primerCluster)
    		return 1;
    	return 0;

    }

    if(!listOpenFile) return 0;

    abierto = collection_list_find(listOpenFile,buscador);

    return abierto;
}

/*
struct timespec obtenerCTime(uint8_t ms, uint16_t hhmmss, uint16_t date) {

        struct timespec res;
        struct tm tiempo;
        uint8_t aux = 0;

        tiempo.

        res.tv_nsec = mktime(&tiempo);


        return res;
}
*/
